package com.zkaler.commons.utils.redis;

import com.zkaler.commons.utils.spring.SpringContextUtil;
import org.springframework.dao.DataAccessException;
import org.springframework.data.redis.connection.RedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.support.collections.DefaultRedisList;
import org.springframework.data.redis.support.collections.DefaultRedisMap;
import org.springframework.data.redis.support.collections.RedisList;
import org.springframework.data.redis.support.collections.RedisMap;
import org.springframework.util.SerializationUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Created by khiker on 2016/7/7.
 */
public class RedisUtils {
    /**
     * 线程内redis
     */
    private static final ThreadLocal<RedisTemplate<String, Object>> currentRedis = new ThreadLocal<>();

    /**
     * 初始化默认redis
     */
    static{
        RedisTemplate<String, Object> redis = (RedisTemplate<String,Object>) SpringContextUtil.getBean("defaultRedis");
        if(redis==null){
            redis=SpringContextUtil.getBean(RedisTemplate.class);
        }
        currentRedis.set(redis);
    }

    /**
     * 切换redis实例
     * @param beanName
     */
    public static void switchRedis(String beanName) {
        RedisTemplate<String, Object> redis = (RedisTemplate<String,Object>) SpringContextUtil.getBean(beanName);
        if(redis!=null){
            currentRedis.set(redis);
        }
    }

    /**
     * 获得redisTemplate
     * @return
     */
    public static RedisTemplate<String,Object> getRedis(){
        return currentRedis.get();
    }

    /**
     * 判断当前实例是否存在key
     * @param key
     * @return
     */
    public static Boolean exists(String key) {
        final byte[] byteKey = key.getBytes();
        return getRedis().execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.exists(byteKey);
            }
        });
    }

    public static Boolean set(final byte[] key, final byte[] value, final long liveTime) {
        return getRedis().execute(new RedisCallback<Boolean>() {
            @Override
            public Boolean doInRedis(RedisConnection connection) throws DataAccessException {
                connection.set(key, value);
                if (liveTime > 0) {
                    connection.expire(key, liveTime);
                }
                return true;
            }
        });
    }
    public static byte[] getBytes(final String key) {
        return getRedis().execute(new RedisCallback<byte[]>() {
            @Override
            public byte[] doInRedis(RedisConnection conn) {
                return conn.get(key.getBytes());
            }
        });
    }

    public static void set(String key, String value, long liveTime) {
        set(key.getBytes(), value.getBytes(), liveTime);
    }

    public static Object get(final String key) {
        return getRedis().execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection conn) {
                byte[] bvalue = conn.get(key.getBytes());
                if (null == bvalue) {
                    return null;
                } else {
                    return getRedis().getStringSerializer().deserialize(bvalue);
                }
            }
        });
    }
    // 设置某一key的超时时间,单位秒
    public static void expired(String key, long timeout) {
        getRedis().expire(key, timeout, TimeUnit.SECONDS);
    }

    public static void del(final String key) {
        getRedis().execute(new RedisCallback<Object>() {
            @Override
            public Object doInRedis(RedisConnection conn) {
                return conn.del(key.getBytes());
            }
        });
    }

    /** 根据正则删除 */
    public static void delByPattern(final String pattern) {
        getRedis().delete(getKeysByPattern(pattern));
    }

    /**
     * 对一个key的value 执行增加一个数字.可以为负数,即减某个数字
     *
     * @param key
     */
    public static Long incrBy(final String key, final Integer byNum) {
        return getRedis().execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.incrBy(key.getBytes(), byNum);
            }
        });
    }

    /** 根据正则获得key */
    public static Set<String> getKeysByPattern(String pattern) {
        return getRedis().keys(pattern);
    }

    /**
     * key以秒为单位,返回给定 key 的剩余生存时间
     *
     * @param key
     */
    public static Long ttl(final String key) {
        return getRedis().execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.ttl(key.getBytes());
            }
        });
    }

    /**
     * 对一个key的value加一
     *
     * @param key
     */
    public static Long incr(final String key) {
        return getRedis().execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.incr(key.getBytes());
            }
        });
    }

    public static Long decr(final String key) {
        return getRedis().execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.decr(key.getBytes());
            }
        });
    }

    public static Long getTtl(final String key) {
        return (Long) getRedis().execute(new RedisCallback<Object>() {
            @Override
            public Long doInRedis(RedisConnection conn) {
                Long value = conn.ttl(key.getBytes());
                return value;
            }
        });
    }
    public static Long getLong(final String key) {
        return getRedis().execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection conn) {
                byte[] bvalue = conn.get(key.getBytes());
                if (null == bvalue) {
                    return null;
                } else {
                    Object obj = getRedis().getStringSerializer().deserialize(bvalue);
                    if (obj == null) {
                        return null;
                    }
                    return Long.parseLong(obj + "");
                }
            }
        });
    }
    
    /**
     * 批量获取
     *
     * @param key
     * @return
     */
    public static List<Object> multiGet(Collection<String> key) {
        return getRedis().opsForValue().multiGet(key);
    }
    /**
     * 将值放入set中
     *
     * @param key
     * @param values
     * @return
     */
    public static Long setSet(String key, Object... values) {
        final byte[] rawKey = key.getBytes();
        final byte[][] rawValues = new byte[values.length][];
        for (int i = 0; i < values.length; i++) {
            rawValues[i] = SerializationUtils.serialize(values[i]);
        }
        return getRedis().execute(new RedisCallback<Long>() {

            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.sAdd(rawKey, rawValues);
            }
        });
    }

    /**
     * 删除set中的数据
     *
     * @param key
     * @param values
     * @return
     */
    public static Long delSet(String key, Object... values) {
        final byte[] rawKey = key.getBytes();
        final byte[][] rawValues = new byte[values.length][];
        for (int i = 0; i < values.length; i++) {
            rawValues[i] = SerializationUtils.serialize(values[i]);
        }
        return getRedis().execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.sRem(rawKey, rawValues);
            }
        });
    }

    /**
     * 获取MAP操作项
     *
     * @return
     */
    public static RedisMap<String, Object> redisMap(String key) {
        return new DefaultRedisMap<String, Object>(key, getRedis());
    }

    /**
     * 获取List操作项
     *
     * @return
     */
    public static RedisList<Object> redisList(String key) {
        return new DefaultRedisList<Object>(key, getRedis());
    }

    /**
     * 取出set的值
     *
     * @param key
     * @param count
     * @return
     */
    public static List<Object> getSet(String key, final long count) {
        final byte[] rawKey = key.getBytes();
        return getRedis().execute(new RedisCallback<List<Object>>() {
            @Override
            public List<Object> doInRedis(RedisConnection connection) throws DataAccessException {
                List<byte[]> list = connection.sRandMember(rawKey, count);
                if (list != null && !list.isEmpty()) {
                    List<Object> result = new ArrayList<Object>();
                    for (byte[] b : list) {
                        result.add(SerializationUtils.deserialize(b));
                    }
                    return result;
                }
                return null;
            }
        });
    }

    /**
     * 获取set的内容
     *
     * @param key
     * @return
     */
    public static List<Object> getSet(String key) {
        final byte[] rawKey = key.getBytes();
        return getRedis().execute(new RedisCallback<List<Object>>() {
            @Override
            public List<Object> doInRedis(RedisConnection connection) throws DataAccessException {
                Set<byte[]> list = connection.sMembers(rawKey);
                if (list != null && !list.isEmpty()) {
                    List<Object> result = new ArrayList<Object>();
                    for (byte[] b : list) {
                        result.add(SerializationUtils.deserialize(b));
                    }
                    return result;
                }
                return null;
            }
        });
    }

    /**
     * 获取hash 所有值
     *
     * @param key
     * @return
     */
    public static Map<String, Object> hgetAll(final String key) {
        return getRedis().execute(new RedisCallback<Map<String, Object>>() {
            @Override
            public Map<String, Object> doInRedis(RedisConnection conn) {
                Map<String, Object> map = new HashMap<String, Object>();
                Map<byte[], byte[]> bvalue = conn.hGetAll(key.getBytes());
                if (null == bvalue) {
                    return null;
                } else {
                    for (Map.Entry<byte[], byte[]> entry : bvalue.entrySet()) {
                        map.put(getRedis().getStringSerializer()
                                        .deserialize(entry.getKey()),
                                getRedis().getStringSerializer()
                                        .deserialize(entry.getValue()));
                    }
                    return map;
                }
            }
        });
    }

    /**
     * 获取hash 所有值
     *
     * @param key
     * @return
     */
    public static List<Object> hMGet(final String key, final byte[]... fields) {
        return getRedis().execute(new RedisCallback<List<Object>>() {
            @Override
            public List<Object> doInRedis(RedisConnection conn) {
                List<Object> res = new ArrayList<>();
                List<byte[]> hMGet = conn.hMGet(key.getBytes(), fields);
                for (byte[] bs : hMGet) {
                    res.add(getRedis().getStringSerializer().deserialize(bs));
                }
                return res;
            }
        });
    }

}
